
#ifndef __VEDA_VJSON_H__
#define __VEDA_VJSON_H__

#include <string>
#include <vector>
#include <map>
#include <stack>
#include <iostream>

#include "VInfra/VDefine.h"

namespace Veda{

class ValueIteratorBase;
class ValueConstIterator;
class ValueIterator;
class CZString;
class StaticString;

enum JsonValueType
{
	nullValue = 0,	///< 'null' value
	intValue,		///< signed integer value
	uintValue,		///< unsigned integer value
	realValue,		///< double value
	stringValue,		///< UTF-8 string value
	booleanValue,	///< bool value
	arrayValue,		///< array value (ordered list)
	objectValue		///< object value (collection of name/value pairs).
};

class VJson
{
	friend class ValueIteratorBase;

public:
	typedef ValueIterator iterator;
	typedef ValueConstIterator const_iterator;
	typedef std::vector<std::string> Members;
	typedef std::map<CZString, VJson> ObjectValues;

	static const VJson null;
	static const int minInt;
	static const int maxInt;
	static const uint maxUInt;

public:
	VJson( JsonValueType type = nullValue );
	VJson( int value );
	VJson( uint value );
	VJson( double value );
	VJson( const char* value );
	VJson( const char* beginValue, const char* endValue );
	VJson( const StaticString& value );
	VJson( const std::string& value );
	VJson( bool value );
	VJson( const VJson& other );
	virtual ~VJson();

	void swap( VJson& other );
	VJson& operator=( const VJson& other );
	bool operator <( const VJson& other ) const;
	bool operator >( const VJson& other ) const;
	bool operator <=( const VJson& other ) const;
	bool operator >=( const VJson& other ) const;
	bool operator ==( const VJson& other ) const;
	bool operator !=( const VJson& other ) const;
	bool operator!() const;	/// Return isNull()
	
	/// Return true if the object has a member named key.
	bool isMember( const char* key ) const;
	/// Return true if the object has a member named key.
	bool isMember( const std::string& key ) const;

	const char* asCString() const;
	std::string asString() const;
	int asInt() const;
	uint asUInt() const;
	double asDouble() const;
	bool asBool() const;

	JsonValueType type() const;
	bool isNull() const;
	bool isBool() const;
	bool isInt() const;
	bool isUInt() const;
	bool isIntegral() const;
	bool isDouble() const;
	bool isNumeric() const;
	bool isString() const;
	bool isArray() const;
	bool isObject() const;
	bool isConvertibleTo( JsonValueType other ) const;

	/// Number of values in array or object
	uint size() const;

	/// \brief Return true if empty array, empty object, or null;
	/// otherwise, false.
	bool empty() const;

	/// Remove all object members and array elements.
	/// \pre type() is arrayValue, objectValue, or nullValue
	/// \post type() is unchanged
	void clear();

	/// Resize the array to size elements. 
	/// New elements are initialized to null.
	/// May only be called on nullValue or arrayValue.
	/// \pre type() is arrayValue or nullValue
	/// \post type() is arrayValue
	void resize( uint size );

	/// \brief Append value to array at the end.
	///
	/// Equivalent to jsonvalue[jsonvalue.size()] = value;
	VJson& append( const VJson& value );
	/// Access an array element (zero based index ).
	/// If the array contains less than index element, then null value are inserted
	/// in the array so that its size is index+1.
	/// (You may need to say 'value[0u]' to get your compiler to distinguish
	///  this from the operator[] which takes a string.)
	VJson& operator[]( uint index );
	/// Access an array element (zero based index )
	/// (You may need to say 'value[0u]' to get your compiler to distinguish
	///  this from the operator[] which takes a string.)
	const VJson& operator[]( uint index ) const;
	/// If the array contains at least index+1 elements, returns the element value, 
	/// otherwise returns defaultValue.
	VJson get( uint index, const VJson& defaultValue ) const;
	/// Return true if index < size().
	bool isValidIndex( uint index ) const;

	/// Access an object value by name, create a null member if it does not exist.
	VJson& operator[]( const char* key );
	/// Access an object value by name, returns null if there is no member with that name.
	const VJson& operator[]( const char* key ) const;
	/// Access an object value by name, create a null member if it does not exist.
	VJson& operator[]( const std::string& key );
	/// Access an object value by name, returns null if there is no member with that name.
	const VJson& operator[]( const std::string& key ) const;
	/// Access an object value by name, create a null member if it does not exist.
	VJson& operator[]( const StaticString& key );

	/// Return the member named key if it exist, defaultValue otherwise.
	VJson get( const char* key, const VJson& defaultValue ) const;
	/// Return the member named key if it exist, defaultValue otherwise.
	VJson get( const std::string& key, const VJson& defaultValue ) const;

	/// \brief Remove and return the named member.  
	///
	/// Do nothing if it did not exist.
	/// \return the removed VJson, or null.
	/// \pre type() is objectValue or nullValue
	/// \post type() is unchanged
	VJson removeMember( const char* key );
	/// Same as removeMember(const char*)
	VJson removeMember( const std::string& key );

	/// \brief Return a list of the member names.
	///
	/// If null, return an empty list.
	/// \pre type() is objectValue or nullValue
	/// \post if type() was nullValue, it remains nullValue
	Members getMemberNames() const;

	std::string toStyledString() const;

	iterator begin();
	iterator end();
	const_iterator begin() const;
	const_iterator end() const;

private:
	union Value_
	{
		int int_;
		uint uint_;
		double real_;
		bool bool_;
		char* string_;
		ObjectValues* map_;
	}value_;
	JsonValueType type_ : 8;
	bool allocated_;

};

std::istream& operator>>( std::istream&, VJson& value );
std::ostream& operator<<( std::ostream&, const VJson& value );


class VJsonReader
{
public:
	VJsonReader();
	virtual ~VJsonReader();

	//same as parse(...)
	virtual bool read( const std::string& document, VJson& root);
	virtual bool read( const char* beginDoc, const char* endDoc, VJson& root);
	virtual bool read( std::istream &is, VJson& root);

	/** \brief Read a VJson from a <a HREF="http://www.json.org">JSON</a> document.
	* \param document UTF-8 encoded string containing the document to read.
	* \param root [out] Contains the root value of the document if it was
	*             successfully parsed.
	* \return \c true if the document was successfully parsed, \c false if an error occurred.
	*/
	virtual bool parse( const std::string& document, VJson& root);

	virtual bool parse( const char* beginDoc, const char* endDoc, VJson& root);

	/// \brief Parse from input stream.
	virtual bool parse( std::istream &is, VJson& root);


private:
	enum TokenType
	{
		tokenEndOfStream = 0,
		tokenObjectBegin,
		tokenObjectEnd,
		tokenArrayBegin,
		tokenArrayEnd,
		tokenString,
		tokenNumber,
		tokenTrue,
		tokenFalse,
		tokenNull,
		tokenArraySeparator,
		tokenMemberSeparator,
		tokenComment
	};

	struct Token
	{
		TokenType type_;
		char* start_;
		char* end_;
	};

	VJson& currentValue();
	char getNextChar();
	void skipSpaces();
	void skipCommentTokens( Token& token );
	bool match( const char* pattern, int patternLength );
	bool readToken( Token& token );
	bool readComment();
	bool readString();
	void readNumber();
	bool readValue();
	bool readObject( Token& token );
	bool readArray( Token& token );
	bool decodeNumber( Token& token );
	bool decodeDouble( Token& token );
	bool decodeString( Token& token );
	bool decodeString( Token& token, std::string& decoded );
	bool decodeUnicodeCodePoint( Token& token, char*& current, char* end, uint& unicode );
	bool decodeUnicodeEscapeSequence( Token& token, char*& current, char* end, uint& unicode );

	typedef std::stack<VJson*> Nodes;
	Nodes nodes_;
	std::string document_;
	char* begin_;
	char* end_;
	char* current_;
};


/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format without formatting (not human friendly).
*
* The JSON document is written in a single line. It is not intended for 'human' consumption,
* but may be usefull to support feature such as RPC where bandwith is limited.
* \sa Reader, Value
*/
class VJsonWriter
{
public:
	VJsonWriter();
	virtual ~VJsonWriter();

	virtual std::string write( const VJson& root );

private:
	void writeValue( const VJson& value );

	std::string document_;
};

/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way.
*
* The rules for line break and indent are as follow:
* - Object value:
*     - if empty then print {} without indent and line break
*     - if not empty the print '{', line break & indent, print one value per line
*       and then unindent and line break and print '}'.
* - Array value:
*     - if empty then print [] without indent and line break
*     - if the array contains no object value, empty array or some other value types,
*       and all the values fit on one lines, then print the array on a single line.
*     - otherwise, it the values do not fit on one line, or the array contains
*       object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their #CommentPlacement.
*
* \sa Reader, Value, Value::setComment()
*/
class VJsonStyledWriter: public VJsonWriter
{
public:
	VJsonStyledWriter();
	virtual ~VJsonStyledWriter();

	virtual std::string write( const VJson& root );

private:
	void writeValue( const VJson& value );
	void writeArrayValue( const VJson& value );
	bool isMultineArray( const VJson& value );
	void pushValue( const std::string& value );
	void writeIndent();
	void writeWithIndent( const std::string& value );

	typedef std::vector<std::string> ChildValues;
	ChildValues childValues_;
	std::string document_;
	std::string indentString_;
	int rightMargin_;
	int indentSize_;
	bool addChildValues_;
};
   
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way,
to a stream rather than to a string.
*
* The rules for line break and indent are as follow:
* - Object value:
*     - if empty then print {} without indent and line break
*     - if not empty the print '{', line break & indent, print one value per line
*       and then unindent and line break and print '}'.
* - Array value:
*     - if empty then print [] without indent and line break
*     - if the array contains no object value, empty array or some other value types,
*       and all the values fit on one lines, then print the array on a single line.
*     - otherwise, it the values do not fit on one line, or the array contains
*       object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their #CommentPlacement.
*
* \param indentation Each level will be indented by this amount extra.
* \sa Reader, Value, Value::setComment()
*/
class VJsonStreamWriter: public VJsonWriter
{
public:
	VJsonStreamWriter();
	VJsonStreamWriter( std::string indentation );
	virtual ~VJsonStreamWriter();

	virtual void write( std::ostream& out, const VJson& root );

private:
	void writeValue( const VJson& value );
	void writeArrayValue( const VJson& value );
	bool isMultineArray( const VJson& value );
	void pushValue( const std::string& value );
	void writeIndent();
	void writeWithIndent( const std::string& value );

	typedef std::vector<std::string> ChildValues;
	ChildValues childValues_;
	std::ostream* document_;
	std::string indentString_;
	int rightMargin_;
	std::string indentation_;
	bool addChildValues_;
};


}

#endif
